#include "cache.h"

/*============================================================================
inicializarCache()
crea la cache para un cierto archivo, s ya existe incrementa el contador,
el cual indica cuantos lo estan utilizando. Se deberia llamar a esta funcion en:
fuse_create y fuse_open
============================================================================*/
void inicializarCache( const char * path ) {
	int32_t 			clustersCacheados, i;
	ST_LISTA_CACHE 		*ptrAuxiliar;

//	printf("se inicializo una cache de archivo\n");
	if( existeCache( path ) )
		return;
//	printf("no existia cache\n");
	clustersCacheados = stPFS.iTamCache / CLUSTER_SIZE; //me va a indicar cuantas ST_CACHE necesito para cachear el archivo
	ST_CACHE * cache = (ST_CACHE *) malloc (sizeof(ST_CACHE) * clustersCacheados);
	memset(cache,0,sizeof(ST_CACHE) * clustersCacheados);

	for(i = 0; i < clustersCacheados; i++) {
		cache[i].numeroCluster = ULTIMO_CLUSTER;
	}

	ptrAuxiliar = (ST_LISTA_CACHE *) malloc (sizeof(ST_LISTA_CACHE));
	strcpy(ptrAuxiliar->path, path);
	ptrAuxiliar->uContador = 1;
	ptrAuxiliar->cache = cache;
	ptrAuxiliar->next = NULL;
	ptrAuxiliar->primerClusterCache = ULTIMO_CLUSTER; //indica EOF, al inicializar esta vacio.
	sem_init(&ptrAuxiliar->semaforoDeUso,0,1);
	agregarCacheAlistaCache( ptrAuxiliar );
}


/*============================================================================
agregarCacheAlistaCache()
Agrega un nodo cache de un archivo a la lista de caches.
============================================================================*/
void agregarCacheAlistaCache( void *nuevaCache) {
	ST_LISTA_CACHE		*pCacheAux = NULL;
	ST_LISTA_CACHE		*pCacheAnterior = NULL;

	ST_LISTA_CACHE * temp = (ST_LISTA_CACHE*) nuevaCache;
//	printf("agregarCacheAlistaCache: tempCacheToPrint->path %s\n",tempCacheToPrint->path);

	sem_wait(&semaforoListaCache);
	pCacheAux = stCache;
	if ( pCacheAux == NULL ){
	//	printf("agregarCacheAlistaCache(): cache inicialmente vacia\n");
		stCache = temp;
	} else {
	//	printf("agregarCacheAlistaCache(): cache actualmente con contenido\n");
		while( pCacheAux != NULL ){
			pCacheAnterior = pCacheAux;
			pCacheAux = pCacheAux->next;
		}
		pCacheAnterior->next = temp;
	}
	sem_post( &semaforoListaCache );
//	printf("agregarCacheAlistaCache(): %s - fin\n", temp->path);
}

/*============================================================================
existeCache()
verifica si existe la cahe de un archivo, si existe incrementa el contador y
retorna 1
============================================================================*/
int existeCache( const char * path ) {
	ST_LISTA_CACHE		*pCacheAux = stCache;
//	printf("existeCache(): inicio\n");
	sem_wait(&semaforoListaCache);
	int returnValue = 0; // zero means not found

	while( pCacheAux != NULL ){
	//	printf("existeCache() path: %s\n", path);
	//	printf("existeCache() pCacheAux->path: %s\n", pCacheAux->path);
		if( strcmp( path, pCacheAux->path ) == 0 ) {
			pCacheAux->uContador++;
			returnValue = 1; // Found !!!
			break;
		}
		pCacheAux = pCacheAux->next;
	}
	sem_post( &semaforoListaCache );
	return returnValue;
}

/*============================================================================
buscarEnListaCaches()
dado el path de un archivo, lo busca en cache
============================================================================*/
void* buscarEnListaCaches( const char * path ) {
	void 				*ptr = NULL;

	sem_wait( &semaforoListaCache);
	ST_LISTA_CACHE 		*pCacheAux = stCache;

	while( pCacheAux != NULL ){
		if( strcmp( path, pCacheAux->path ) == 0 ) {
			pCacheAux->next = NULL;
			ptr = pCacheAux;
			break;
		}
		pCacheAux = pCacheAux->next;
	}
	sem_post( &semaforoListaCache );
//	printf("busco en cache: %s\n", path);
	return ptr;
}

/*============================================================================
leerDesdeCache()
reemplaza en cache por el menos utilizado.
============================================================================*/
int leerDesdeCache(const char * path,uint32_t clusterAleer,ST_CLUSTER cluster) {
	int32_t 			clustersCacheados;

	ST_LISTA_CACHE 		*ptrAuxiliar = buscarEnListaCaches( path );
//printf("leerDesdeCache()-inicio: %s\n",ptrAuxiliar->path);
	if ( ptrAuxiliar != NULL )  {
		printf("encontro el archivo en cache\n");
		sem_wait(&ptrAuxiliar->semaforoDeUso);

		clustersCacheados = stPFS.iTamCache / CLUSTER_SIZE;

		if ( clusterAleer >= ptrAuxiliar->primerClusterCache && clusterAleer < (clustersCacheados+ptrAuxiliar->primerClusterCache)){
			memcpy(cluster,ptrAuxiliar->cache[clusterAleer - ptrAuxiliar->primerClusterCache].bufferCache ,CLUSTER_SIZE);
			sem_post(&ptrAuxiliar->semaforoDeUso);
			return 1;
		}

		sem_post(&ptrAuxiliar->semaforoDeUso);
		return 0;
	}
	return 0;
}

/*============================================================================
eliminarCacheDeListaCaches()
elimina una cache de archivo de la lista de caches.
============================================================================*/
void destruirCacheDeArchivo(const char * path) {
	ST_LISTA_CACHE		*pCacheAnterior = NULL;

	sem_wait( &semaforoListaCache);
	ST_LISTA_CACHE		*pCacheAux = stCache;

	while( pCacheAux != NULL ){

		if( strcmp( path, pCacheAux->path ) == 0 ) {

			pCacheAux->uContador = pCacheAux->uContador - 1;

			if ( pCacheAux->uContador > 0 ) {
				sem_post( &semaforoListaCache);
				return;
			} else {
				if ( pCacheAnterior == NULL ) {
					stCache = stCache->next;
					sem_post( &semaforoListaCache );
					return;
				} else {
					pCacheAnterior->next = pCacheAux->next;
					pCacheAux->next = NULL;
					pCacheAux->cache = NULL;
					sem_post( &semaforoListaCache);
					return;
				}
			}

		}  // fin if de comparacion
		pCacheAnterior = pCacheAux;
		pCacheAux = pCacheAux->next;
	}
	sem_post( &semaforoListaCache);
}


/*============================================================================
escribirEnCache()
carga cache, retorna 1 si carga, retorna 0 si no carga, carga hasta agotar espacio
============================================================================*/
int escribirEnCache(const char * path,uint32_t clusterAescribir,ST_CLUSTER cluster) {
	int32_t clustersCacheados;

	ST_LISTA_CACHE *ptrAuxiliar = buscarEnListaCaches( path );

	if ( ptrAuxiliar != NULL ) {
		sem_wait(&ptrAuxiliar->semaforoDeUso);

		clustersCacheados = stPFS.iTamCache / CLUSTER_SIZE;

		if ( ptrAuxiliar->primerClusterCache == ULTIMO_CLUSTER ) //indica EOF, al inicializar esta vacio.
			ptrAuxiliar->primerClusterCache = clusterAescribir;

		if ( clusterAescribir >= ptrAuxiliar->primerClusterCache && clusterAescribir < (clustersCacheados+ptrAuxiliar->primerClusterCache)){
			memcpy(ptrAuxiliar->cache[clusterAescribir - ptrAuxiliar->primerClusterCache].bufferCache, cluster ,CLUSTER_SIZE);
			ptrAuxiliar->cache[clusterAescribir - ptrAuxiliar->primerClusterCache].numeroCluster = clusterAescribir;
			sem_post(&ptrAuxiliar->semaforoDeUso);
			return 1;
		}

		sem_post(&ptrAuxiliar->semaforoDeUso);
		return 0;
	}
	return 0;
}


/*============================================================================
crearCacheDump()
============================================================================*/
int crearCacheDump() {

	FILE		*pFichero;
	int32_t 	clustersCacheados, i, cantidadBloques = 0;
	time_t		tiempo;
	struct tm	*ptm;

	time(&tiempo);
	ptm = gmtime(&tiempo );

	/*Abre el archivo para escribir, si no existia lo crea, si existia lo borra*/
	if( (pFichero = fopen( "cache_dump.txt", "a" )) == NULL ){
		return 0;
	}

	sem_wait( &semaforoListaCache);
	ST_LISTA_CACHE 		*ptrAuxiliar = stCache;

	clustersCacheados = stPFS.iTamCache / CLUSTER_SIZE; //me va a indicar cuantas ST_CACHE necesito para cachear el archivo

	if(stCache == NULL) {
		fprintf(pFichero, "Timestamp: [%4d.%2d.%2d %2d:%02d:%02d]\n", ptm->tm_year, ptm->tm_mon, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec );
		fprintf(pFichero, "La cache se encuentra vacia\n");
	}

	while( ptrAuxiliar != NULL ) {
		fprintf(pFichero, "Timestamp: [%4d.%2d.%2d %2d:%02d:%02d]\n", ptm->tm_year, ptm->tm_mon, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec );
		fprintf(pFichero, "Archivo: %s\n",ptrAuxiliar->path);
		fprintf(pFichero, "Tamanio de bloque de Cache: 4096\n");

		printf("estoy dentro del while\n");
		for( i = 0; i < clustersCacheados; i++ ) {
			if (ptrAuxiliar->cache[i].numeroCluster != ULTIMO_CLUSTER)
				cantidadBloques++;
		}
		printf("crearCacheDump() - cantidad de caches: %i", cantidadBloques);
		fprintf(pFichero,"Cantidad de bloques de Cache: %i\n",cantidadBloques+1);
		for( i = 0; i < clustersCacheados; i++ ) {
			if (ptrAuxiliar->cache[i].numeroCluster != ULTIMO_CLUSTER)
				fprintf(pFichero,"Contenido de bloque de Cache %i: %s\n",i+1,(char*)ptrAuxiliar->cache[i].bufferCache);
		}
		ptrAuxiliar = ptrAuxiliar->next;
	}

	sem_post( &semaforoListaCache);

	fclose( pFichero );
	return 1;
}
